#!/usr/bin/python -S
"""
base.py
"""

__author__ = 'Andy Chu'


import collections
import sys

import ioloop
import log


class Error(Exception):
  pass


class EventEmitter(object):
  """
  Copy node API

  - catch exceptions and emit error here?
  
  This code is very small:

  https://github.com/tmpvar/node-eventemitter/blob/master/index.js
  """
  # Events emitted
  EMITS = []

  def __init__(self, event_loop=None):
    self.event_loop = event_loop or ioloop.IOLoop.instance()
    self.listeners = collections.defaultdict(list)
    # event counters for debugging
    self._counters = collections.defaultdict(int)

  def _check(self, event):
    if event not in self.EMITS:
      raise Error('%r is not emitted by %s; should be one of: %s' %
          (event, self.__class__, self.EMITS))

  def on(self, event, listener):
    """Register a function that figures on a given event for this emitter.

    Args
      event: string e.g. 'exit', 'data', 'close'
      listener: callable
    """
    self._check(event)
    self.listeners[event].append(listener)

  On = on
  AddListener = on  # for symmetry

  def off(self, event, listener):
    self._check(event)
    # This is a linear search -- we could use a better algorithm there are ever
    # a large number of listeners (probably not)
    listeners = self.listeners.get(event)
    if listeners is None:
      raise Error('EventEmitter has no listeners for event %r' % event)
    try:
      listeners.remove(listener)
      log.trace("Removed %s for %r", listener, event)
    except ValueError:
      log.trace("%s isn't registered for %r", listener, event)

  remove_listener = off
  RemoveListener = off

  def all_off(self, event=None):
    """
    Remove all listeners for a given event.  If no event specified, remove all
    listeners for this EventEmitter.
    """
    if event is None:  # Remove ALL listeners for ALL events
      self.listeners.clear()
    else:
      # Remove all listeners for the given event
      try:
        del self.listeners[event]
      except ValueError:
        pass

  def emit(self, event, *args, **kwargs):
    """
    This does 1 -> n "fan out" for multiple listeners.
    """
    self._check(event)
    self._counters[event] += 1
    listeners = self.listeners[event]
    for li in listeners:
      log.trace('calling listener %s', li)
      li(*args, **kwargs)

  Emit = emit

  def counters(self):
    return self._counters


class ReadableStream(EventEmitter):
  """
  events: data, error, close, end (no more data)

  e.g. stdout
  """
  def __init__(self, fd):
    pass
  # have to call self.emit() some time here, shit

  # self.Emit('data')  when the select loop wakes up.  every time this is
  # created
  # when you start a process, you add multiple HANDLERS.  One is the
  # ChildProcess
  # other is ReadableStream for stdout
  # other is WriteableStream for stdin?

  # self.Emit('end') when there is no more data


class WriteableStream(EventEmitter):
  """
  events:
  drain, error, close, pipe

  e.g. stdin
  """
  pass
